home *** CD-ROM | disk | FTP | other *** search
/ Language/OS - Multiplatform Resource Library / LANGUAGE OS.iso / a_utils / _archvrs / unix / shar.lha / shar / makekit.c < prev    next >
C/C++ Source or Header  |  1987-12-03  |  10KB  |  413 lines

  1. /*
  2. **  MAKEKIT
  3. **  Split up source files into reasonably-sized shar lists.
  4. **
  5. **  Options:
  6. **    -e        Leave our output file out
  7. **    -h #        Number of header lines in input file
  8. **    -i name        Input file name
  9. **    -k #        Maximum number of archives desired
  10. **    -m        Same as "-i Manifest -o Manifest -s2"
  11. **    -n name        Name for resultant archives
  12. **    -o name        Output file name
  13. **    -p        Preserve original input order
  14. **    -s #[k]        Maximum size of each archvie
  15. **    -t text        Set final instructions after all are unpacked
  16. **    -x        Don't actually do the shar'ing
  17. */
  18. #include "shar.h"
  19. RCS("$Header: makekit.c,v 1.15 87/03/13 12:56:41 rs Exp $")
  20.  
  21.  
  22. /*
  23. **  Our block of information about the files we're doing.
  24. */
  25. typedef struct {
  26.     char    *Name;            /* Filename            */
  27.     char    *Text;            /* What it is            */
  28.     int         Where;            /* Where it is            */
  29.     int         Type;            /* Directory or file?        */
  30.     long     Size;            /* Size in bytes        */
  31. } BLOCK;
  32.  
  33.  
  34. /*
  35. **  Our block of information about the archives we're making.
  36. */
  37. typedef struct {
  38.     int         Count;            /* Number of files        */
  39.     long     Size;            /* Bytes used by archive    */
  40. } ARCHIVE;
  41.  
  42.  
  43. /*
  44. **  We re-use parts of one buffer to hold three different strings in our
  45. **  argument vector to exec().
  46. */
  47. #define SEG0         0        /* Ending archive number    */
  48. #define SEG1        10        /* Current archive number    */
  49. #define SEG2        20        /* Output name (rest of buff)    */
  50.  
  51. /*
  52. **  Format strings; these are strict K&R so you shouldn't have to change them.
  53. */
  54. #define FORMAT1        " %-25s%2d\t%s\n"
  55. #define FORMAT2        "%s%2.2d"
  56.  
  57.  
  58. /*
  59. **  Global variables.
  60. */
  61. char    *InName;            /* File with list to pack    */
  62. char    *OutName;            /* Where our output goes    */
  63. char    *SharName = "Part";        /* Prefix for name of each shar    */
  64. char    *Trailer;            /* Text for shar to pack in    */
  65. char     TEMP[] = "/tmp/arkXXXXXX";    /* Temporary manifest file    */
  66. int     ArchCount = 20;        /* Max number of archives    */
  67. int     ExcludeIt;            /* Leave out the output file?    */
  68. int     Header;            /* Lines of prolog in input    */
  69. int     Preserve;            /* Preserve order for Manifest?    */
  70. int     Working = TRUE;        /* Call shar when done?        */
  71. long     Size = 55000;            /* Largest legal archive size    */
  72.  
  73.  
  74. /*
  75. **  Sorting predicate to put README first, then directories, then large
  76. **  files, then smaller files, which is how we want to assign things to
  77. **  the archives.
  78. */
  79. static int
  80. SizeP(t1, t2)
  81.     BLOCK    *t1;
  82.     BLOCK    *t2;
  83. {
  84.     long     i;
  85.  
  86.     if (EQ(t1->Name, "README") || EQ(t1->Name, "readme"))
  87.     return(-1);
  88.     if (EQ(t2->Name, "README") || EQ(t1->Name, "readme"))
  89.     return(1);
  90.     if (t1->Type != t2->Type)
  91.     return(t1->Type == F_DIR ? 1 : -1);
  92.     return((i = t1->Size - t2->Size) == 0L ? 0 : (i < 0L ? -1 : 1));
  93. }
  94.  
  95.  
  96. /*
  97. **  Sorting predicate to get things in alphabetical order, which is how
  98. **  we write the Manifest file.
  99. */
  100. static int
  101. NameP(t1, t2)
  102.     BLOCK    *t1;
  103.     BLOCK    *t2;
  104. {
  105.     int         i;
  106.  
  107.     return((i = *t1->Name - *t2->Name) ? i : strcmp(t1->Name, t2->Name));
  108. }
  109.  
  110.  
  111. /*
  112. **  Skip whitespace.
  113. */
  114. static char *
  115. Skip(p)
  116.     register char    *p;
  117. {
  118.     while (*p && WHITE(*p))
  119.     p++;
  120.     return(p);
  121. }
  122.  
  123.  
  124. /*
  125. **  Signal handler.  Clean up and die.
  126. */
  127. static
  128. Catch(s)
  129.     int         s;
  130. {
  131.     int         e;
  132.  
  133.     e = errno;
  134.     (void)unlink(TEMP);
  135.     fprintf(stderr, "Got signal %d, %s.\n", s, Ermsg(e));
  136.     exit(1);
  137. }
  138.  
  139.  
  140. main(ac, av)
  141.     register int     ac;
  142.     char        *av[];
  143. {
  144.     register FILE    *F;
  145.     register FILE    *In;
  146.     register BLOCK    *t;
  147.     register ARCHIVE    *k;
  148.     register char    *p;
  149.     register int     i;
  150.     register int     lines;
  151.     register int     Value;
  152.     BLOCK        *Table;
  153.     BLOCK        *TabEnd;
  154.     ARCHIVE        *Ark;
  155.     ARCHIVE        *ArkEnd;
  156.     char         buff[BUFSIZ];
  157.     int             LastOne;
  158.     int             Start;
  159.  
  160.     /* Collect input. */
  161.     Value = FALSE;
  162.     while ((i = getopt(ac, av, "eh:i:k:n:mop:s:t:x")) != EOF)
  163.     switch (i) {
  164.         default:
  165.         exit(1);
  166.         case 'e':
  167.         ExcludeIt = TRUE;
  168.         break;
  169.         case 'h':
  170.         Header = atoi(optarg);
  171.         break;
  172.         case 'i':
  173.         InName = optarg;
  174.         break;
  175.         case 'k':
  176.         ArchCount = atoi(optarg);
  177.         break;
  178.         case 'm':
  179.         InName = OutName = "MANIFEST";
  180.         Header = 2;
  181.         break;
  182.         case 'n':
  183.         SharName = optarg;
  184.         break;
  185.         case 'o':
  186.         OutName = optarg;
  187.         break;
  188.         case 'p':
  189.         Preserve = TRUE;
  190.         break;
  191.         case 's':
  192.         Size = atoi(optarg);
  193.         if (IDX(optarg, 'k') || IDX(optarg, 'K'))
  194.             Size *= 1024;
  195.         break;
  196.         case 't':
  197.         Trailer = optarg;
  198.         break;
  199.         case 'x':
  200.         Working = FALSE;
  201.         break;
  202.     }
  203.     ac -= optind;
  204.     av += optind;
  205.  
  206.     /* Write the file list to a temp file. */
  207.     F = fopen(mktemp(TEMP), "w");
  208.     SetSigs(TRUE, Catch);
  209.     if (av[0])
  210.     /* Got the arguments on the command line. */
  211.     while (*av)
  212.         fprintf(F, "%s\n", *av++);
  213.     else {
  214.     /* Got the name of the file from the command line. */
  215.     if (InName == NULL)
  216.         In = stdin;
  217.     else if ((In = fopen(InName, "r")) == NULL) {
  218.         fprintf(stderr, "Can't read %s as manifest, %s.\n",
  219.             InName, Ermsg(errno));
  220.         exit(1);
  221.     }
  222.     /* Skip any possible prolog, then output rest of file. */
  223.     while (--Header >= 0 && fgets(buff, sizeof buff, In))
  224.         ;
  225.     if (feof(In)) {
  226.         fprintf(stderr, "Nothing but header lines in list!?\n");
  227.         exit(1);
  228.     }
  229.     while (fgets(buff, sizeof buff, In))
  230.         fputs(buff, F);
  231.     if (In != stdin)
  232.         (void)fclose(In);
  233.     }
  234.     (void)fclose(F);
  235.  
  236.     /* Count number of files, allow for NULL and our output file. */
  237.     F = fopen(TEMP, "r");
  238.     for (lines = 2; fgets(buff, sizeof buff, F); lines++)
  239.     ;
  240.     rewind(F);
  241.  
  242.     /* Read lines and parse lines, see if we found our OutFile. */
  243.     Table = NEW(BLOCK, lines);
  244.     for (t = Table, Value = FALSE, lines = 0; fgets(buff, sizeof buff, F); ) {
  245.     /* Read line, skip first word, check for blank line. */
  246.     if (p = IDX(buff, '\n'))
  247.         *p = '\0';
  248.     else
  249.         fprintf(stderr, "Warning, line truncated:\n%s\n", buff);
  250.     p = Skip(buff);
  251.     if (*p == '\0')
  252.         continue;
  253.  
  254.     /* Copy the line, snip off the first word. */
  255.     for (p = t->Name = COPY(p); *p && !WHITE(*p); p++)
  256.         ;
  257.     if (*p)
  258.         *p++ = '\0';
  259.  
  260.     /* Skip <spaces><digits><spaces>; remainder is the file description. */
  261.     for (p = Skip(p); *p && isdigit(*p); )
  262.         p++;
  263.     t->Text = Skip(p);
  264.  
  265.     /* Get file type. */
  266.     if (!GetStat(t->Name)) {
  267.         fprintf(stderr, "Can't stat %s (%s), skipping.\n",
  268.             t->Name, Ermsg(errno));
  269.         continue;
  270.     }
  271.     t->Type = Ftype(t->Name);
  272.  
  273.     /* Guesstimate it's size when archived. */
  274.     t->Size = strlen(t->Name) * 3 + 200;
  275.     if (t->Type == F_FILE) {
  276.         long i = Fsize(t->Name);
  277.         t->Size += i + i / 60;
  278.     }
  279.     if (t->Size > Size) {
  280.         fprintf(stderr, "At %ld bytes, %s is too big for any archive!\n",
  281.             t->Size, t->Name);
  282.         exit(1);
  283.     }
  284.  
  285.     /* Is our ouput file there? */
  286.     if (!Value && OutName && EQ(OutName, t->Name))
  287.         Value = TRUE;
  288.  
  289.     /* All done -- advance to next entry. */
  290.     t++;
  291.     }
  292.     (void)fclose(F);
  293.     (void)unlink(TEMP);
  294.     SetSigs(S_RESET, (int (*)())NULL);
  295.  
  296.     /* Add our output file? */
  297.     if (!ExcludeIt && !Value && OutName) {
  298.     t->Name = OutName;
  299.     t->Text = "This shipping list";
  300.     t->Type = F_FILE;
  301.     t->Size = lines * 60;
  302.     t++;
  303.     }
  304.  
  305.     /* Sort by size, get archive space. */
  306.     lines = t - Table;
  307.     TabEnd = &Table[lines];
  308.     if (!Preserve)
  309.     qsort((char *)Table, lines, sizeof Table[0], SizeP);
  310.     Ark = NEW(ARCHIVE, ArchCount);
  311.     ArkEnd = &Ark[ArchCount];
  312.  
  313.     /* Loop through the pieces, and put everyone into an archive. */
  314.     for (t = Table; t < TabEnd; t++) {
  315.     for (k = Ark; k < ArkEnd; k++)
  316.         if (t->Size + k->Size < Size) {
  317.         k->Size += t->Size;
  318.         t->Where = k - Ark;
  319.         k->Count++;
  320.         break;
  321.         }
  322.     if (k == ArkEnd) {
  323.         fprintf(stderr, "'%s' doesn't fit -- need more then %d archives.\n",
  324.             t->Name, ArchCount);
  325.         exit(1);
  326.     }
  327.     /* Since our share doesn't build sub-directories... */
  328.     if (t->Type == F_DIR && k != Ark)
  329.         fprintf(stderr, "Warning:  directory '%s' is in archive %d\n",
  330.             t->Name, k - Ark + 1);
  331.     }
  332.  
  333.     /* Open the output file. */
  334.     if (OutName == NULL)
  335.     F = stdout;
  336.     else {
  337.     if (GetStat(OutName)) {
  338.         /* Handle /foo/bar/VeryLongFileName.BAK for non-BSD sites. */
  339.         (void)sprintf(buff, "%s.BAK", OutName);
  340.         p = (p = RDX(buff, '/')) ? p + 1 : buff;
  341.         if (strlen(p) > 14)
  342.         /* ... well, sort of handle it. */
  343.         (void)strcpy(&p[10], ".BAK");
  344.         fprintf(stderr, "Renaming %s to %s\n", OutName, buff);
  345.         (void)unlink(buff);
  346.         (void)link(OutName, buff);
  347.         (void)unlink(OutName);
  348.     }
  349.     if ((F = fopen(OutName, "w")) == NULL) {
  350.         fprintf(stderr, "Can't open '%s' for output, %s.\n",
  351.             OutName, Ermsg(errno));
  352.         exit(1);
  353.     }
  354.     }
  355.  
  356.     /* Sort the shipping list, then write it. */
  357.     if (!Preserve)
  358.     qsort((char *)Table, lines, sizeof Table[0], NameP);
  359.     fprintf(F, "   File Name\t\tArchive #\tDescription\n");
  360.     fprintf(F, "-----------------------------------------------------------\n");
  361.     for (t = Table; t < TabEnd; t++)
  362.     fprintf(F, FORMAT1, t->Name, t->Where + 1, t->Text);
  363.  
  364.     /* Close output.  Are we done? */
  365.     if (F != stdout)
  366.     (void)fclose(F);
  367.     if (!Working)
  368.     exit(0);
  369.  
  370.     /* Find last archive number. */
  371.     for (i = 0, t = Table; t < TabEnd; t++)
  372.     if (i < t->Where)
  373.         i = t->Where;
  374.     LastOne = i + 1;
  375.  
  376.     /* Find archive with most files in it. */
  377.     for (i = 0, k = Ark; k < ArkEnd; k++)
  378.     if (i < k->Count)
  379.         i = k->Count;
  380.  
  381.     /* Build the fixed part of the argument vector. */
  382.     av = NEW(char*, i + 10);
  383.     av[0] = "shar";
  384.     i = 1;
  385.     if (Trailer) {
  386.     av[i++] = "-t";
  387.     av[i++] = Trailer;
  388.     }
  389.     (void)sprintf(&buff[SEG0], "%d", LastOne);
  390.     av[i++] = "-e";
  391.     av[i++] = &buff[SEG0];
  392.     av[i++] = "-n";
  393.     av[i++] = &buff[SEG1];
  394.     av[i++] = "-o";
  395.     av[i++] = &buff[SEG2];
  396.  
  397.     /* Call shar to package up each archive. */
  398.     for (Start = i, i = 0; i < LastOne; i++) {
  399.     (void)sprintf(&buff[SEG1], "%d", i + 1);
  400.     (void)sprintf(&buff[SEG2], FORMAT2, SharName, i + 1);
  401.     for (lines = Start, t = Table; t < TabEnd; t++)
  402.         if (t->Where == i)
  403.         av[lines++] = t->Name;
  404.     av[lines] = NULL;
  405.     fprintf(stderr, "Packing kit %d...\n", i + 1);
  406.     if (lines = Execute(av))
  407.         fprintf(stderr, "Warning:  shar returned status %d.\n", lines);
  408.     }
  409.  
  410.     /* That's all she wrote. */
  411.     exit(0);
  412. }
  413.